package gui.customizer.panes.netfilter;

import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

import net.SSHConnection;
import net.SSHConnectionProxy;
import net.miginfocom.swing.MigLayout;
import core.models.FilterRule;

public class FilterRuleEditor extends JPanel {
	/**
	 * 
	 */
	private static final long serialVersionUID = -3736943501516191298L;
	private JTextField txtSourceAddress;
	private JTextField txtDestinationAddress;
	private JTextField txtSourcePort;
	private JTextField txtDestinationPort;
	private JTextField txtState;
	private JComboBox cmbChain;
	private JComboBox cmbTarget;
	private JComboBox cmbInInterface;
	private JCheckBox chckbxInInterfaceInv;
	private JComboBox cmbOutInterface;
	private JCheckBox chckbxOutInterfaceInv;
	private JComboBox cmbProtocol;
	private JCheckBox chckbxProtocolInv;
	private JCheckBox chckbxSourceAddress;
	private JCheckBox chckbxDestinationAddress;
	private JCheckBox chckbxSourcePort;
	private JCheckBox chckbxDestinationPort;
	private JCheckBox chckbxState;
	private JButton btnApplyChanges;

	public FilterRuleEditor() {
		setLayout(new MigLayout("", "[100px:200px][]25[100px:200px][]", "[][]5[][]5[][]5[][]5[][]5[][]5[]"));
		
		JLabel lblChain = new JLabel("Chain");
		add(lblChain, "cell 0 0");
		
		cmbChain = new JComboBox();
		add(cmbChain, "cell 0 1,growx");
		
		JLabel lblTarget = new JLabel("Target");
		add(lblTarget, "cell 2 0");
		
		cmbTarget = new JComboBox();
		add(cmbTarget, "cell 2 1,growx");
		
		JLabel lblInputInterface = new JLabel("Input Interface");
		add(lblInputInterface, "cell 0 2");
		
		cmbInInterface = new JComboBox();
		add(cmbInInterface, "cell 0 3,growx");
		
		chckbxInInterfaceInv = new JCheckBox();
		chckbxInInterfaceInv.setText("!");
		add(chckbxInInterfaceInv, "cell 1 3,alignx center");
		
		JLabel lblOutputInterface = new JLabel("Output Interface");
		add(lblOutputInterface, "cell 2 2");
		
		cmbOutInterface = new JComboBox();
		add(cmbOutInterface, "cell 2 3,growx");
		
		chckbxOutInterfaceInv = new JCheckBox();
		chckbxOutInterfaceInv.setText("!");
		add(chckbxOutInterfaceInv, "cell 3 3");
		
		JLabel lblProtocol = new JLabel("Protocol");
		add(lblProtocol, "cell 0 4");
		
		cmbProtocol = new JComboBox();
		cmbProtocol.addItemListener(new ItemListener() {
			@Override
			public void itemStateChanged(ItemEvent e) {
				if (e.getStateChange() == ItemEvent.SELECTED) {
					if (e.getItem().equals("tcp") || e.getItem().equals("udp")) {
						txtSourcePort.setEnabled(true);
						txtDestinationPort.setEnabled(true);
					} else {
						txtSourcePort.setEnabled(false);
						txtDestinationPort.setEnabled(false);
					}
				}
			}
		});
		add(cmbProtocol, "cell 0 5,growx");
		
		chckbxProtocolInv = new JCheckBox("!");
		add(chckbxProtocolInv, "cell 1 5");
		
		JLabel lblSourceAddress = new JLabel("Source address");
		add(lblSourceAddress, "cell 0 6");
		
		txtSourceAddress = new JTextField();
		add(txtSourceAddress, "cell 0 7,growx");
		txtSourceAddress.setColumns(10);
		
		chckbxSourceAddress = new JCheckBox("!");
		add(chckbxSourceAddress, "cell 1 7");
		
		JLabel lblDestinationAddress = new JLabel("Destination address");
		add(lblDestinationAddress, "cell 2 6");
		
		txtDestinationAddress = new JTextField();
		add(txtDestinationAddress, "cell 2 7,growx");
		txtDestinationAddress.setColumns(10);
		
		chckbxDestinationAddress = new JCheckBox("!");
		add(chckbxDestinationAddress, "cell 3 7");
		
		JLabel lblSourcePort = new JLabel("Source port");
		add(lblSourcePort, "cell 0 8");
		
		txtSourcePort = new JTextField();
		add(txtSourcePort, "cell 0 9,growx");
		txtSourcePort.setColumns(10);
		txtSourcePort.setEnabled(false);
		
		chckbxSourcePort = new JCheckBox("!");
		add(chckbxSourcePort, "cell 1 9");
		
		JLabel lblDestinationPort = new JLabel("Destination Port");
		add(lblDestinationPort, "cell 2 8");
		
		txtDestinationPort = new JTextField();
		add(txtDestinationPort, "cell 2 9,growx");
		txtDestinationPort.setColumns(10);
		txtDestinationPort.setEnabled(false);
		
		chckbxDestinationPort = new JCheckBox("!");
		add(chckbxDestinationPort, "cell 3 9");
		
		JLabel lblState = new JLabel("State");
		add(lblState, "cell 0 10");
		
		txtState = new JTextField();
		add(txtState, "cell 0 11,growx");
		txtState.setColumns(10);
		
		chckbxState = new JCheckBox("!");
		add(chckbxState, "cell 1 11");
		
		btnApplyChanges = new JButton("Apply changes");
		add(btnApplyChanges, "cell 0 12");
		btnApplyChanges.setVisible(false);	
		try {
			initializeCheckboxes();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void setRule(FilterRule rule) {
		HashMap<String, String> options = rule.getOptions();
		
		// Chain
		cmbChain.setSelectedItem(rule.getChain());
		
		// Target
		cmbTarget.setSelectedItem(options.get("-j").split(" ")[0]);
		
		// Input Interface
		if (options.containsKey("-i")) {
			String iface = options.get("-i");
			cmbInInterface.setSelectedItem(iface);
			chckbxInInterfaceInv.setSelected(false);
		} else if (options.containsKey("! -i")) {
			cmbInInterface.setSelectedItem(options.get("! -i"));
			chckbxInInterfaceInv.setSelected(true);
		} else {
			cmbInInterface.setSelectedItem("-none-");
			chckbxInInterfaceInv.setSelected(false);
		}
		
		// Output Interface
		if (options.containsKey("-o")) {
			cmbOutInterface.setSelectedItem(options.get("-o"));
			chckbxOutInterfaceInv.setSelected(false);
		} else if (options.containsKey("! -o")) {
			cmbOutInterface.setSelectedItem(options.get("! -o"));
			chckbxOutInterfaceInv.setSelected(true);
		} else {
			cmbOutInterface.setSelectedItem("-none-");
			chckbxOutInterfaceInv.setSelected(false);
		}
		
		// Port
		if (options.containsKey("-p")) {
			cmbProtocol.setSelectedItem(options.get("-p"));
			chckbxProtocolInv.setSelected(false);
		} else if (options.containsKey("! -p")) {
			cmbProtocol.setSelectedItem(options.get("! -p"));
			chckbxProtocolInv.setSelected(true);
		} else {
			cmbProtocol.setSelectedItem("-none-");
			chckbxProtocolInv.setSelected(false);
		}
		
		// Source Address
		if (options.containsKey("-s")) {
			txtSourceAddress.setText(options.get("-s"));
			chckbxSourceAddress.setSelected(false);
		} else if (options.containsKey("! -s")) {
			txtSourceAddress.setText(options.get("! -s"));
			chckbxSourceAddress.setSelected(true);
		} else {
			txtSourceAddress.setText("");
			chckbxSourceAddress.setSelected(false);
		}
		
		// Destination Address
		if (options.containsKey("-d")) {
			txtDestinationAddress.setText(options.get("-d"));
			chckbxDestinationAddress.setSelected(false);
		} else if (options.containsKey("! -d")) {
			txtDestinationAddress.setText(options.get("! -d"));
			chckbxDestinationAddress.setSelected(true);
		} else {
			txtDestinationAddress.setText("");
			chckbxDestinationAddress.setSelected(false);
		}
		
		// Ports
		if (options.containsKey("-m tcp") || options.containsKey("-m udp")) {
			txtSourcePort.setText("");
			chckbxSourcePort.setSelected(false);
			txtDestinationPort.setText("");
			chckbxDestinationPort.setSelected(false);
			String value = options.containsKey("-m tcp") ? options.get("-m tcp") : options.get("-m udp");
			String[] elements = value.split(" ");
			int i = 0;
			while (i < elements.length) {
				if (elements[i].equals("!")) {
					setPortTextFields(elements, i+1, true);
					i += 3;
				} else {
					setPortTextFields(elements, i, false);
					i += 2;
				}
			}
		} else {
			txtSourcePort.setText("");
			chckbxSourcePort.setSelected(false);
			txtDestinationPort.setText("");
			chckbxDestinationPort.setSelected(false);
		}
		
		
		// State
		if (options.containsKey("-m state")) {
			String[] elements = options.get("-m state").split(" ");
			if (elements[0].equals("--state")) {
				txtState.setText(elements[1]);
				chckbxState.setSelected(false);
			} else if (elements[0].equals("!") && elements[1].equals("--state")) {
				txtState.setText(elements[2]);
				chckbxState.setSelected(true);
			}
		} else {
			txtState.setText("");
			chckbxState.setSelected(false);
		}
		
		btnApplyChanges.setVisible(true);
	}
	
	private void setPortTextFields(String[] elements, int first, boolean inv) {
		if (elements[first].equals("--sport")) {
			txtSourcePort.setText(elements[first+1]);
			chckbxSourcePort.setSelected(inv);
		} else if (elements[first].equals("--dport")) {
			txtDestinationPort.setText(elements[first+1]);
			chckbxDestinationPort.setSelected(inv);
		}
	}
	
	private void initializeCheckboxes() throws IOException {
		// Required
		cmbChain.setModel(new DefaultComboBoxModel(FilterRule.getChains()));
		cmbTarget.setModel(new DefaultComboBoxModel(FilterRule.getTargets()));
		
		cmbProtocol.setModel(new DefaultComboBoxModel(new String[] { "-none-", "tcp", "udp", "icmp" }));
		
		// Optional
		if (!SSHConnectionProxy.isConnected())
			return;
		SSHConnection conn = new SSHConnectionProxy();
		
		List<String> interfaces = conn.getInterfacesNames();
		
		DefaultComboBoxModel inInterfacesModel = new DefaultComboBoxModel();
		DefaultComboBoxModel outInterfacesModel = new DefaultComboBoxModel();
		inInterfacesModel.addElement("-none-");
		outInterfacesModel.addElement("-none-");
		for (String iface : interfaces) {
			inInterfacesModel.addElement(iface);
			outInterfacesModel.addElement(iface);
		}
		
		cmbInInterface.setModel(inInterfacesModel);
		cmbOutInterface.setModel(outInterfacesModel);
	}
	
	public FilterRule getRule() {
		// Chain
		FilterRule rule = new FilterRule("-A", (String) cmbChain.getSelectedItem());
		
		// Interfaces
		if (!cmbInInterface.getSelectedItem().equals("-none-")) {
			if (chckbxInInterfaceInv.isSelected())
				rule.addOption("! -i", (String) cmbInInterface.getSelectedItem());
			else
				rule.addOption("-i", (String) cmbInInterface.getSelectedItem());
		}
		
		if (!cmbOutInterface.getSelectedItem().equals("-none-")) {
			if (chckbxOutInterfaceInv.isSelected())
				rule.addOption("! -o", (String) cmbOutInterface.getSelectedItem());
			else
				rule.addOption("-o", (String) cmbOutInterface.getSelectedItem());
		}
		
		// Protocol
		if (!cmbProtocol.getSelectedItem().equals("-none-")) {
			if (chckbxProtocolInv.isSelected())
				rule.addOption("! -p", (String) cmbProtocol.getSelectedItem());
			else
				rule.addOption("-p", (String) cmbProtocol.getSelectedItem());
		}
		
		// Source address
		if (!txtSourceAddress.getText().equals("")) {
			if (chckbxSourceAddress.isSelected())
				rule.addOption("! -s", txtSourceAddress.getText());
			else
				rule.addOption("-s", txtSourceAddress.getText());
		}
		
		// Destination address
		if (!txtDestinationAddress.getText().equals("")) {
			if (chckbxDestinationAddress.isSelected())
				rule.addOption("! -s", txtDestinationAddress.getText());
			else
				rule.addOption("-s", txtDestinationAddress.getText());
		}
		
		// Source and dest ports
		if (cmbProtocol.getSelectedItem().equals("tcp") || cmbProtocol.getSelectedItem().equals("udp")) {
			StringBuffer optVal = new StringBuffer();
			if (!txtSourcePort.getText().equals("")) {
				if (chckbxSourcePort.isSelected())
					optVal.append("! ");
				optVal.append("--sport ");
				optVal.append(txtSourcePort.getText());
			}
			
			if (!txtDestinationPort.getText().equals("")) {
				if (optVal.length() != 0) optVal.append(" ");
				if (chckbxDestinationPort.isSelected())
					optVal.append("! ");
				optVal.append("--dport ");
				optVal.append(txtDestinationPort.getText());
			}
			
			if (!txtSourcePort.getText().equals("") || !txtDestinationPort.getText().equals("")) {
				rule.addOption("-m " + (String) cmbProtocol.getSelectedItem(), optVal.toString());
			}
		}
		
		// State
		if (!txtState.getText().equals("")) {
			if (chckbxState.isSelected())
				rule.addOption("-m state", "! --state " + txtState.getText());
			else
				rule.addOption("-m state", "--state" + txtState.getText());
		}
		
		// Target
		rule.addOption("-j", (String) cmbTarget.getSelectedItem());
		
		return rule;
	}
	
	public void hideApplyButton() {
		btnApplyChanges.setVisible(false);
	}
	
	public void addListenerToApplyButton(ActionListener listener) {
		btnApplyChanges.addActionListener(listener);
	}

}
